package client.socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Iterator;

import date.util.FormatDate;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class Client {

	private final String SERVER = "127.0.0.1";//主机
	private final int PORT = 8888;//端口

	//消息类型标志
	private final String TALK = "Talk";
	private final String UP = "Up";
	private final String DOWN = "Down";
	private final String SERVERCLOSE = "ServerClose";

	private Socket socket;//客户socket
	private BufferedWriter writer;//写
	private BufferedReader reader;//读

	private DatagramSocket UDPServer;//客户端UDPserver
	private SimpleStringProperty userName;//用户名
	private String UDPIp;//UDPserver的ip和端口
	private int UDPPort;//UDPServer监听的端口

	private HashMap<String, String> userMap = new HashMap<>();//在线用户map

	private boolean isServerClosed = false;//服务器状态标记

	private ObservableList<String> userList = FXCollections.observableArrayList();//用户列表

	private SimpleStringProperty receMsg = new SimpleStringProperty();//接收消息字符串

	public Client(SimpleStringProperty userName, ObservableList<String> userList, SimpleStringProperty receMsg) {
		this.userName = userName;
		this.userList = userList;
		this.receMsg = receMsg;
	}
	/**
	 * 登录
	 * @return
	 */
	public boolean login() {
		try {
			socket = new Socket(this.SERVER, this.PORT);
			isServerClosed = false;
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			writer = new BufferedWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
			ReaderThread readerThread = new ReaderThread();//读取服务器发来的消息
			readerThread.start();

			//启动客户端UDPserver
			while (true) {
				try {
					//获取随机端口号
					int port = (int) (System.currentTimeMillis() % 64512) + 1024;
					UDPServer = new DatagramSocket(port);
					UDPPort = port;
					break;
				} catch (SocketException e) {
					// e.printStackTrace();
				}
			}
			try {
				this.UDPIp = InetAddress.getLocalHost().getHostAddress();
			} catch (UnknownHostException e) {
				// e.printStackTrace();
			}
			userName.set("user" + UDPPort);
			Platform.runLater(() -> {
				sendToServer("Login," + userName.get() + "," + UDPIp + ":" + UDPPort);
			});
			UDPReceiverThread receiverThread = new UDPReceiverThread(UDPServer);
			receiverThread.start();
			return true;
		} catch (Exception e) {
			// e.printStackTrace();
			return false;
		}
	}
	/**
	 * 退出
	 */
	public void logout() {
		close();
		broadcast();
		userMap.clear();
		Platform.runLater(() -> {
			userList.clear();
		});

	}
	/**
	 * 关闭与服务器对的连接
	 */
	public void close() {
		try {
			if (!isServerClosed) {
				sendToServer("Logout," + userName.get() + "," + UDPIp + ":" + UDPPort);
			}
			reader.close();
			writer.close();
			socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
	/**
	 * 发送消息给服务器
	 * @param msg
	 */
	public void sendToServer(String msg) {
		try {
			writer.write(msg);
			writer.newLine();
			writer.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
	/**
	 * 广播消息给其他用户
	 */
	public void broadcast() {
		if (userMap.size() != 0) {
			Iterator<String> userList = userMap.keySet().iterator();
			String msg = userName.get() + ":" + "Down," + userName.get() + "," + UDPIp + ":" + UDPPort;
			while (userList.hasNext()) {
				String user = userList.next();
				String[] str = userMap.get(user).split(":");
				String ip = str[0];
				int port = Integer.valueOf(str[1]);
				try {
					InetAddress address = InetAddress.getByName(ip);
					UDPSenderThread senderThread = new UDPSenderThread(address, port, msg);
					senderThread.start();

				} catch (UnknownHostException e) {
				}
			}
		}

	}
	/*
	 * 发消息给单个用户
	 */
	public void sendToUser(String user, String msg) {
		String[] str = userMap.get(user).split(":");
		String ip = str[0];
		int port = Integer.valueOf(str[1]);
		try {
			InetAddress address = InetAddress.getByName(ip);
			UDPSenderThread senderThread = new UDPSenderThread(address, port, userName.get() + ":" + msg);
			senderThread.start();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}

	}
	
	/**
	 * 接收服务器消息的线程
	 * @author huangyk
	 *
	 */
	class ReaderThread extends Thread {

		@Override
		public void run() {
			try {
				while (true) {
					String msg = reader.readLine();
					if (msg != null) {
						if (handleMessage(msg)) {
							isServerClosed = true;
							close();
							// System.out.println("服务器关闭");
							Platform.runLater(() -> {
								receMsg.set(FormatDate.getDate() + "\n"
										+ "Server has closed!"+ "\r\n");
							});
							break;
						}
					}
				}

			} catch (IOException e) {
				// System.out.println("client异常退出");
				isServerClosed = false;
				// close();

			}

		}
		/**
		 * 消息处理
		 * @param msg
		 * @return
		 */
		public boolean handleMessage(String msg) {
			String[] str = msg.split(",");
			boolean flag = false;
			switch (str[0].trim()) {
			case TALK:
				break;
			case UP:
				userMap.put(str[1], str[2]);
				Platform.runLater(() -> {
					userList.add(str[1]);
				});
				break;
			case DOWN:
				userMap.remove(str[1]);
				Platform.runLater(() -> {
					userList.remove(str[1]);
				});
				break;
			case SERVERCLOSE:
				flag = true;
				break;
			default:
				break;
			}
			return flag;
		}
	}

	/**
	 * UDP发送线程
	 * @author huangyk
	 *
	 */
	class UDPSenderThread extends Thread {
		private InetAddress server;
		private DatagramSocket socket;
		private int port;

		private String msg;

		public UDPSenderThread(InetAddress address, int port, String msg) {
			this.server = address;
			this.port = port;
			try {
				this.socket = new DatagramSocket();
			} catch (SocketException e) {
				e.printStackTrace();
			}
			this.socket.connect(server, this.port);
			this.msg = msg;
		}

		@Override
		public void run() {

			try {
				byte[] data = msg.getBytes("UTF-8");
				DatagramPacket output = new DatagramPacket(data, data.length, this.server, this.port);
				this.socket.send(output);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * UDP接收线程
	 * @author huangyk
	 *
	 */
	class UDPReceiverThread extends Thread {
		private DatagramSocket socket;

		UDPReceiverThread(DatagramSocket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			byte[] buffer = new byte[65507];
			while (true) {
				DatagramPacket input = new DatagramPacket(buffer, buffer.length);
				try {
					socket.receive(input);
					String msg = new String(input.getData(), 0, input.getLength(), "UTF-8");
					handlerMessage(msg);
					Platform.runLater(() -> {
						receMsg.set(FormatDate.getDate() + " " + "From:" + msg.substring(0, msg.indexOf(":")) + "\n"
								+ msg.substring(msg.indexOf(":") + 1) + "\r\n");
					});

				} catch (IOException e) {
					// System.out.println("客户端UDPServer关闭");
					break;
				}
			}
			if (!socket.isClosed()) {
				socket.close();
			}

		}
		/**
		 * 消息处理
		 * @param msg
		 */
		public void handlerMessage(String msg) {
			String[] str = msg.substring(msg.indexOf(":") + 1).split(",");
			if (str[0].equals(DOWN)) {
				userMap.remove(str[1]);
				Platform.runLater(() -> {
					userList.remove(str[1]);
				});
			}
		}
	}

}
